//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_Script.h".
//-------------------------------------------------------------------

#ifndef _UXS_SCRIPT_ENUM_H
#define _UXS_SCRIPT_ENUM_H

#pragma once

#include "ScriptEngine.h"

namespace UX_STL
{
namespace UXScript
{
	/*
		Example:

		#define ENUM_LIST( build ) \
			build( ONE, ) \
			build( TWO, ) \
			build( THREE, =3 )

		UX_SCRIPT_ENUM( Enum, ENUM_LIST, );

		#undef ENUM_LIST
	*/

#	define __UX_BUILD_ENUM_VALUE( name, value, ... ) \
		name value,

#	define __UX_BUILD_ENUM_CASE( name, ... ) \
		case name :  return #name;

#	define __UX_BUILD_ENUM_COUNT( ... ) \
		+1

#	define __UX_BUILD_ENUM_GET_VALUE( name, ... ) \
		name,
	
#	define __UX_BUILD_ENUM_GET_NAME( name, ... ) \
		#name,

#	define __UX_BUILD_ENUM_GET_INDEX( name, ... ) \
		if ( ++i and value == name )	return i-1;


#	define UX_SCRIPT_ENUM( enumName, enumList, enumMethods ) \
		struct enumName \
		{ \
		/* types */ \
		public: \
			enum type { \
				enumList( __UX_BUILD_ENUM_VALUE ) \
			}; \
			\
			typedef enumName							Self; \
			\
			typedef uni_array< const type >				values_arr_t; \
			typedef uni_array< char const * const >		names_arr_t; \
			typedef NearInt::Unsigned< type >::type		value_t; \
			\
			\
		/* constants */ \
		public: \
			static const usize	_count = enumList( __UX_BUILD_ENUM_COUNT ); \
			\
			\
		/* compile-time methods */ \
		private: \
			static const type * _Values () { \
				static const type	_values[] = { enumList( __UX_BUILD_ENUM_GET_VALUE ) }; \
				return _values; \
			} \
			\
			static char const * const * _Names () { \
				static char const * const	_names[] = { enumList( __UX_BUILD_ENUM_GET_NAME ) }; \
				return _names; \
			} \
			\
		public: \
			static uni_c_string Name () { \
				return #enumName; \
			} \
			\
			static constexpr usize Count () { \
				return _count; \
			} \
			\
			static values_arr_t Values () { \
				return values_arr_t( (type *)_Values(), Count() ); \
			} \
			\
			static names_arr_t ValuesNames () { \
				return names_arr_t( _Names(), Count() ); \
			} \
			\
			template <usize Index> \
			static constexpr type Get () \
			{ \
				STATIC_ASSERTE( Index < _count ); \
				\
				return _Values()[ Index ]; \
			} \
			\
			template <usize Index> \
			static uni_c_string GetString () \
			{ \
				STATIC_ASSERTE( Index < _count ); \
				\
				return _ValueNames()[ Index ]; \
			} \
			\
			template <type Value> \
			static usize IndexOf () \
			{ \
				const type value = Value; \
				usize i = 0; \
				\
				enumList( __UX_BUILD_ENUM_GET_INDEX ) \
				\
				STATIC_WARNING( "no index for value" ); \
				return -1; \
			} \
			\
			\
		/* runtime methods*/ \
		public: \
			static type Get (usize index) \
			{ \
				if ( index < _count ) \
					return _Values()[ index ]; \
				else { \
					WARNING( "invalid index" ); \
					return type(0); \
				} \
			} \
			\
			static uni_c_string GetString (usize index) \
			{ \
				if ( index < _count ) \
					return _Names()[ index ]; \
				else { \
					WARNING( "invalid index" ); \
					return uni_c_string(); \
				} \
			} \
			\
			static usize IndexOf (type value) \
			{ \
				usize i = 0; \
				\
				enumList( __UX_BUILD_ENUM_GET_INDEX ) \
				\
				WARNING( "no index for value" ); \
				return -1; \
			} \
			\
			static uni_c_string ToString (type value) \
			{ \
				switch ( value ) \
				{ \
					enumList( __UX_BUILD_ENUM_CASE ) \
				} \
				WARNING( "invalid enum value" ); \
				return uni_c_string(); \
			} \
			\
			static type FromString (uni_c_string str) \
			{ \
				for (usize i = 0; i < Count(); ++i) { \
					if ( str == ValuesNames()[i] ) \
						return Values()[i]; \
				} \
				WARNING( "string does not match with any value" ); \
				return type(0); \
			} \
			\
			static bool Bind (ScriptEngine &se) \
			{ \
				int	res = se->RegisterEnum( Name().cstr() ); \
				\
				if ( res < 0 and res != AngelScript::asALREADY_REGISTERED ) { \
					AS_CALL( res ); \
					return false; \
				} \
				\
				for (usize i = 0; i < Count(); ++i) { \
					AS_CALL( se->RegisterEnumValue( Name().cstr(), ValuesNames()[i], Values()[i] ) ); \
				} \
				return true; \
			} \
			\
			\
		/* variables */ \
		private: \
			union { \
				type	_visible; \
				value_t	_value; \
			}; \
			\
			\
		/* methods */ \
		public: \
			enumName () : _visible(Get<0>()) {} \
			\
			enumName (type value) : _visible(value) {} \
			\
			template <typename T> \
			explicit \
			enumName (const T& value) : _visible(type(value)) {} \
			\
			Self & Inc () { \
				_visible = Get( IndexOf( _visible )+1 ); \
				return *this; \
			} \
			\
			Self Next () const { \
				return Self(*this).Inc(); \
			} \
			\
			Self & Dec () { \
				_visible = Get( IndexOf( _visible )-1 ); \
				return *this; \
			} \
			\
			Self Prev () const { \
				return Self(*this).Dec(); \
			} \
			\
			\
		/* operators */ \
		public: \
			_UX_DIM_ALL_INTEGER_OPERATORS_SELF( _value ) \
			_UX_DIM_ALL_INTEGER_OPERATORS_TYPE( _value, type, ) \
			\
			\
		/* user methods*/ \
		public: \
			enumMethods \
		}


}	// UXScript
}	// UX_STL

#endif	// _UXS_SCRIPT_ENUM_H